package cn.anecansaitin.not_enough_harness.common.harness_data;

import cn.anecansaitin.not_enough_harness.common.ModDataMap;
import cn.anecansaitin.not_enough_harness.common.item.ModComponents;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.Util;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.NotNull;

import java.util.List;
import java.util.stream.IntStream;

public record SprintableHarness(int cd, int duration, float forwardSpeed,
                                float backwardSpeed, float leftSpeed,
                                float rightSpeed,
                                float upSpeed, float downSpeed) {
    public static final SprintableHarness ZERO = new SprintableHarness(0, 0, 0, 0);
    public static final Codec<SprintableHarness> CODEC =
            RecordCodecBuilder.create(instance ->
                    instance.group(
                                    Codec.INT.fieldOf("cd").forGetter(SprintableHarness::cd),
                                    Codec.INT.fieldOf("duration").forGetter(SprintableHarness::duration),
                                    Codec.FLOAT.fieldOf("forward_speed").forGetter(SprintableHarness::forwardSpeed),
                                    Codec.FLOAT.fieldOf("backward_speed").forGetter(SprintableHarness::backwardSpeed),
                                    Codec.FLOAT.fieldOf("left_speed").forGetter(SprintableHarness::leftSpeed),
                                    Codec.FLOAT.fieldOf("right_speed").forGetter(SprintableHarness::rightSpeed),
                                    Codec.FLOAT.fieldOf("up_speed").forGetter(SprintableHarness::upSpeed),
                                    Codec.FLOAT.fieldOf("down_speed").forGetter(SprintableHarness::downSpeed)
                            )
                            .apply(instance, SprintableHarness::new)
            );

    public static final Codec<SprintableHarness> SYNC_CODEC =
            RecordCodecBuilder.create(instance ->
                    instance.group(
                                    Codec.INT_STREAM.fieldOf("0").forGetter(data -> IntStream.of(data.cd, data.duration)),
                                    Codec.list(Codec.FLOAT, 6, 6).fieldOf("1").forGetter(data -> List.of(data.forwardSpeed, data.backwardSpeed, data.leftSpeed, data.rightSpeed, data.upSpeed, data.downSpeed))
                            )
                            .apply(instance, (intStream, floats) -> {
                                int[] ints = Util.fixedSize(intStream, 2).getOrThrow();
                                List<Float> speeds = Util.fixedSize(floats, 6).getOrThrow();
                                return new SprintableHarness(ints[0], ints[1], speeds.getFirst(), speeds.get(1), speeds.get(2), speeds.get(3), speeds.get(4), speeds.get(5));
                            })
            );

    public static final StreamCodec<FriendlyByteBuf, SprintableHarness> STREAM_CODEC =
            StreamCodec.composite(
                    ByteBufCodecs.VAR_INT, SprintableHarness::cd,
                    ByteBufCodecs.VAR_INT, SprintableHarness::duration,
                    ByteBufCodecs.FLOAT, SprintableHarness::forwardSpeed,
                    ByteBufCodecs.FLOAT, SprintableHarness::backwardSpeed,
                    ByteBufCodecs.FLOAT, SprintableHarness::leftSpeed,
                    ByteBufCodecs.FLOAT, SprintableHarness::rightSpeed,
                    ByteBufCodecs.FLOAT, SprintableHarness::upSpeed,
                    ByteBufCodecs.FLOAT, SprintableHarness::downSpeed,
                    SprintableHarness::new
            );

    public SprintableHarness(int cd, int duration, float horizontalSpeed, float verticalSpeed) {
        this(cd, duration, horizontalSpeed, horizontalSpeed, horizontalSpeed, horizontalSpeed, verticalSpeed, verticalSpeed);
    }

    @NotNull
    public static SprintableHarness getComponentOrDataMapAndSet(ItemStack item) {
        SprintableHarness sprintableHarness = item.get(ModComponents.SPRINTABLE_HARNESS);

        if (sprintableHarness == null) {
            SprintableHarness dataMap = item.getItemHolder().getData(ModDataMap.SPRINTABLE_HARNESS);

            if (dataMap == null) {
                dataMap = SprintableHarness.ZERO;
            }

            item.set(ModComponents.SPRINTABLE_HARNESS, dataMap);
            return dataMap;
        }

        return sprintableHarness;
    }
}
